package org.honeynet.hbbackend.maxmindgeoip;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.sql.Connection;

import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.ejb.MessageDriven;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.jms.BytesMessage;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.sql.DataSource;

import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.honeynet.hbbackend.common.types.common.AttackInfoType;
import org.honeynet.hbbackend.common.types.maxmindgeoip.json.GeoipAttackRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.maxmind.geoip.Location;

@MessageDriven(
		mappedName = "jms/new_attack"
//		activationConfig = {
//				@ActivationConfigProperty(propertyName="subscriptionDurability", propertyValue="Durable"),
//				@ActivationConfigProperty(propertyName="subscriptionName", propertyValue="maxmindgeoip"),
//				@ActivationConfigProperty(propertyName="clientId", propertyValue="maxmindgeoip")
//		}
)
//@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) //TODO
public class AttackLookup implements MessageListener {
	
	Logger log = LoggerFactory.getLogger(AttackLookup.class);
	
//	@Resource(mappedName="jdbc/hbbackend")
//	private DataSource ds;
	
	@Resource(mappedName="jms/new_attack_geoip")
	private Destination jms_new_geoip;
	
	@Resource(mappedName="jms/ConnectionFactory")
	private ConnectionFactory jmsConnectionFactory;
	
	@EJB
	LookupServiceWrapper geoDb;
	
	private static String[] submithttpIps = { "target_ip", "source_ip", "sensor_ip" };
	
//	Connection db = null;
	javax.jms.Connection mq = null;
	Session sess = null;
	MessageProducer prod = null;
	
	ObjectMapper om = new ObjectMapper();
	
	
	@Override
	public void onMessage(Message msg) {
		
		try {
			mq = jmsConnectionFactory.createConnection();
			sess = mq.createSession(true, 0);
			prod = sess.createProducer(jms_new_geoip);
			
			String msgType = msg.getJMSType();

			if (msgType.equals(AttackInfoType.submithttp.name)) {
				submithttpWork(msg);
			} else {
				log.error("ignoring unknown msg type: {}", msgType);
			}
			
		} catch (JMSException e) {
			log.error("got JMSException", e);
			throw new EJBException(e);
		} finally {
			try {
				if (mq != null) mq.close();
			} catch (JMSException e) {
				log.error("got JMSException", e);
				throw new EJBException(e);
			}
		}
	}
	
	
	private void submithttpWork(Message msg) throws JMSException {
		for (int i = 0 ; i < submithttpIps.length ; i++) {
			String ip = msg.getStringProperty(submithttpIps[i]);
			String id = msg.getStringProperty("id");
			String md5 = msg.getStringProperty("md5");
			
			LocationWrapper lw = geoDb.getLocation(ip);
			if (lw.location == null) {
				log.info("couldn't resolve ip = {} ({}_id = {} md5 = {})", new Object[] { ip, AttackInfoType.submithttp.table, id, md5 });
				continue;
			}
			
			log.trace("resolved ip = {} ({}_id = {} md5 = {})", new Object[] { ip, AttackInfoType.submithttp.table, id, md5 });
			
//			BytesMessage newMsg = sess.createBytesMessage();
			TextMessage newMsg = sess.createTextMessage();
			newMsg.setStringProperty("ip", ip);
			newMsg.setStringProperty("md5", md5);
			newMsg.setStringProperty("c_type", AttackInfoType.submithttp.table);
			newMsg.setStringProperty("c_id", id);
			newMsg.setStringProperty("c_prop", submithttpIps[i]);
			
			GeoipAttackRecord result;
			try {
				TextMessage inMsg = (TextMessage) msg;
//				BytesMessage inMsg = (BytesMessage) msg;
//				byte buf[] = new byte[(int) inMsg.getBodyLength()];
//				inMsg.readBytes(buf);
//				JsonNode jsonAttackRecord = om.readValue(buf, 0, buf.length, JsonNode.class);
				JsonNode jsonAttackRecord = om.readValue(inMsg.getText(), JsonNode.class);
				
				result = new GeoipAttackRecord();
				result.ip = ip;
				result.geoip = lw.location;
				result.attack = jsonAttackRecord;

//				newMsg.writeBytes(om.writeValueAsBytes(result));
				newMsg.setText(om.writeValueAsString(result));
				
			} catch (JsonParseException e) {
				log.error("got JsonParseException", e);
				throw new EJBException(e);
			} catch (JsonMappingException e) {
				log.error("got JsonMappingException", e);
				throw new EJBException(e);
			} catch (IOException e) {
				log.error("got IOException", e);
				throw new EJBException(e);
			}
			
			send(newMsg);
		}
	}
	
	
	private void send(Message msg) throws JMSException {
		log.trace("sending new message to new_attack_geoip");
		msg.setJMSType("maxmind");
		prod.setDeliveryMode(DeliveryMode.NON_PERSISTENT); //TODO
		prod.send(msg);
	}
}
